/*
 * @Date: 2022-02-14 14:34:50
 * @LastEditors: 春贰
 * @gitee: https://gitee.com/chun22222222
 * @github: https://github.com/chun222
 * @Desc:
 * @LastEditTime: 2023-11-16 16:29:30
 * @FilePath: \go-ezcode-admin\system\core\db\db.go
 */
package db

import (
	"fmt"
	"os"

	"time"

	"go-ezcode-admin/system/core/config"
	"go-ezcode-admin/system/core/log"
	"go-ezcode-admin/system/model/DbModel"
	"go-ezcode-admin/system/util/file"
	"go-ezcode-admin/system/util/sys"
	"gorm.io/driver/mysql"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

var connGlobal *gorm.DB

var basedir = sys.ExecutePath() + "\\" //根目录

func Instance() *gorm.DB {
	//判断连接是否断开
	if connGlobal == nil {
		InitConn()
	} else {
		dbSql, _ := connGlobal.DB()
		if err := dbSql.Ping(); err != nil {
			log.Intrance().Error("数据库断开链接" + err.Error())
			// 连接断开，重新初始化
			dbSql.Close()
			InitConn()
		}
	}
	return connGlobal
}

func InitConn() {
	switch config.Instance().DB.DBType {
	//后期拓展别的数据库
	case "mysql":
		t_connGlobal, err := GormMysql()
		if err != nil {
			//继续
			log.Intrance().Error("重新连接中……" + err.Error())
			time.Sleep(5 * time.Second)
			InitConn()
		} else {
			connGlobal = t_connGlobal
		}

	case "sqlite":
		t_connGlobal, err := GormSqlite()
		if err != nil {
			// 处理错误
			log.Intrance().Fatal(err.Error())
		}
		connGlobal = t_connGlobal

	default:
		log.Intrance().Fatal("No DBType")
	}
}

var err error

func GormSqlite() (*gorm.DB, error) {
	db, err := gorm.Open(sqlite.Open(fmt.Sprintf("%s.db", config.Instance().DB.DBName)), &gorm.Config{})
	return db, err
}

func GormMysql() (*gorm.DB, error) {
	var conn *gorm.DB
	m := config.Instance().DB
	if m.DBName == "" {
		return nil, fmt.Errorf("名称不能为空")
	}
	dsn := m.DBUser + ":" + m.DBPwd + "@tcp(" + m.DBHost + ")/" //

	conn, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})

	if err != nil {
		log.Intrance().Error(err.Error())
		return nil, err
	}
	conn.Exec("CREATE DATABASE  IF NOT EXISTS `" + m.DBName + "`") //先创建
	dsn = m.DBUser + ":" + m.DBPwd + "@tcp(" + m.DBHost + ")/" + m.DBName + "?charset=utf8mb4&parseTime=True&loc=Local"

	conn, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger:                                   LogConfig(),
		PrepareStmt:                              true,
		DisableForeignKeyConstraintWhenMigrating: true, //是否禁用数据库迁移外键约束，AutoMigrate 会自动创建数据库外键约束，您可以在初始化时禁用此功能
		NamingStrategy: schema.NamingStrategy{
			//TablePrefix:   "t_",                              // 表前缀
			SingularTable: true, // 不添加后缀s
			//NoLowerCase:   true, // 取消驼峰
		},
	})
	if err != nil {
		log.Intrance().Fatal(err.Error())
		return nil, err
	}
	sqlDB, _ := conn.DB()
	sqlDB.SetMaxIdleConns(200)              //保留的空闲连接数量
	sqlDB.SetMaxOpenConns(1000)             //最大打开的连接数量
	sqlDB.SetConnMaxLifetime(time.Hour * 1) //最大连接周期这个值比mysql设置的超时时间需要小，这里设置成1小时回收，mysql必须设置超过1小时wait_timeout=28800
	//interactive_timeout=28800
	//设置事务隔离级别
	conn.Exec("SET global TRANSACTION ISOLATION LEVEL READ COMMITTED") //读取已提交
	//读取串行化
	//conn.Exec("SET global TRANSACTION ISOLATION LEVEL SERIALIZABLE")
	//设置数据库执行模式
	conn.Exec("set @@sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'")
	return conn, nil
}

//初始化表
func InitSystemTables() {
	CheckTableData(
		&DbModel.SysUser{},
		&DbModel.SysLoginLog{},
		&DbModel.SysRole{},
		&DbModel.SysUserRole{},
		&DbModel.SysUserFastMenu{},
		&DbModel.SysPermission{},
		&DbModel.SysRolePermission{},
		&DbModel.SysRoleMenu{},
		&DbModel.SysUserPermission{},
		&DbModel.SysRecord{},
		&DbModel.SysConfig{},
	)

	if file.CheckNotExist(basedir + "install_lock") {
		initData()
		os.Create(basedir + "install_lock")
	}

}
func CheckTableData(tbs ...interface{}) {
	//检查表是否存在，不存在则创建
	conn := Instance()
	for _, tb := range tbs {
	 
		if !conn.Migrator().HasTable(tb) {
			if err := conn.Migrator().CreateTable(tb); err != nil {
				log.Intrance().Fatal("init database1 error: " + err.Error())
			}
		} else {
			//关闭自动加字段
			if err := conn.AutoMigrate(tb); err != nil {
				log.Intrance().Fatal("init database1 error: " + err.Error())
			}
		}
	 
	}
	//关闭连接
	sqlDB, _ := conn.DB()
	sqlDB.Close()
}

//原生查询返回结果到map
func QueryMap(sql string) ([]map[string]interface{}, error) {
	rows, _ := Instance().Raw(sql).Rows()
	cols, _ := rows.Columns()
	types, _ := rows.ColumnTypes()

	var r []map[string]interface{}
	for rows.Next() {
		m := make(map[string]interface{})
		columns := make([]interface{}, len(cols))
		columnPointers := make([]interface{}, len(cols))
		for i, _ := range columns {
			columnPointers[i] = &columns[i]
		}

		if err := rows.Scan(columnPointers...); err != nil {
			return nil, err
		}

		for i, colName := range cols {
			//这里类型有很多 不细处理  插入反正没问题
			//gorm真难用
			if types[i].DatabaseTypeName() == "DATETIME" {
				val := columnPointers[i].(*interface{})
				if *val != nil {
					m[colName] = (*val).(time.Time).Format("2006-01-02 15:04:05")
				} else {
					m[colName] = *val
				}
			} else {
				val := columnPointers[i].(*interface{})
				m[colName] = *val
			}

		}
		r = append(r, m)
	}
	return r, nil
}

//读取sql 初始化
func initData() {
	sqlfile := basedir + "custom\\databackup\\init.sql"
	if !file.CheckNotExist(sqlfile) {
		Import(sqlfile)
	}
}
